package pts

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"time"

	uuid "github.com/satori/go.uuid"

	"github.com/fatih/color"
	"github.com/rs/xid"
)

// StrToFloat64 string转float64
func StrToFloat64(str string) (f64 float64) {
	f64, _ = strconv.ParseFloat(str, 64)
	return
}

// StrToFloat32 string转float32
func StrToFloat32(str string) (f32 float32) {
	return float32(StrToFloat64(str))
}

func Now() string {
	return time.Now().Format(`2006-01-02 15:04:05`)
}

// Print 输出白色字体，多个参数以\t分隔
func Print(msg ...any) {
	var m []any
	for i, s := range msg {
		if i > 0 {
			m = append(m, "\t", s)
		} else {
			m = append(m, s)
		}
	}
	_, _ = colour.get(color.FgWhite).Println(m...)
}

// PrintColor 输出指定颜色字体，多个参数以\t分隔
func PrintColor(attr color.Attribute, msg ...any) {
	var m []any
	for i, s := range msg {
		if i > 0 {
			m = append(m, "\t", s)
		} else {
			m = append(m, s)
		}
	}
	_, _ = colour.get(attr).Println(m...)
}

// PrintRed 输出红色字体，多个参数以\t分隔
func PrintRed(msg ...any) {
	PrintColor(color.FgRed, msg...)
}

// PrintGreen 输出绿色字体，多个参数以\t分隔
func PrintGreen(msg ...any) {
	PrintColor(color.FgGreen, msg...)
}

// PrintBlue 输出蓝色字体，多个参数以\t分隔
func PrintBlue(msg ...any) {
	PrintColor(color.FgBlue, msg...)
}

func Log(msg ...any) {
	PrintColor(color.FgWhite, append([]any{Now()}, msg...)...)
}

func LogColor(attr color.Attribute, msg ...any) {
	PrintColor(attr, append([]any{Now()}, msg...)...)
}

// LogRed 输出红色字体，多个参数以\t分隔
func LogRed(msg ...any) {
	PrintColor(color.FgRed, append([]any{Now()}, msg...)...)
}

// LogGreen 输出绿色字体，多个参数以\t分隔
func LogGreen(msg ...any) {
	PrintColor(color.FgGreen, append([]any{Now()}, msg...)...)
}

// LogBlue 输出绿色字体，多个参数以\t分隔
func LogBlue(msg ...any) {
	PrintColor(color.FgBlue, append([]any{Now()}, msg...)...)
}

// JSON 对json数据进行格式化（带缩进）
func JSON(data any) string {
	b, _ := json.MarshalIndent(data, "", "    ")
	return string(b)
}

// PrintJSON 对json数据进行格式化（带缩进），并打印
func PrintJSON(data any) (j string) {
	j = JSON(data)
	fmt.Println(j)
	return
}

// NewErr 生成error，多个参数以：分隔
func NewErr(strs ...any) error {
	return fmt.Errorf(ArrJoin(strs, "："))
}

// Space 返回指定数量的空格
func Space(n int) string {
	return strings.Join(make([]string, n+1), " ")
}

// Str 转为string
func Str(v any) string {
	str := fmt.Sprintf("%+v", v)
	return IIf(str == "<nil>", "", str)
}

// IIf 三元：当条件为true时，返回ifTrue，否则返回ifFalse
func IIf[T any](condition bool, ifTrue, ifFalse T) T {
	if condition {
		return ifTrue
	} else {
		return ifFalse
	}
}

// Decode 类似oracle的decode、php8的match
//
// - 参数为两个一组，依次往后匹配
//
// 1. 若某组的首个值与val相等，则返回该组第2个值
//
// 2. 若某组仅1个值，则直接返回该值
//
// 3. 否则返回与val相同类型的空值
func Decode[T comparable](val T, matches ...T) (v T) {
	var res T
	for i, l := 0, len(matches); i < l; i += 2 {
		if i == l-1 {
			return matches[i]
		} else if matches[i] == val {
			return matches[i+1]
		}
	}
	return res
}

// Nvl 返回参数中第1个不为空的值
//
// - 类似oracle的nvl
func Nvl[T comparable](val T, vals ...T) T {
	var empty T

	if val != empty {
		return val
	}

	for _, s := range vals {
		if s != empty {
			return s
		}
	}

	return val
}

// Trim 清除字符串首尾的指定内容
//
// - 默认cutest "[\\s\\t\\r\\n]+"
func Trim(str string, cutset ...string) string {
	if len(cutset) == 0 {
		cutset = []string{"[\\s\\t\\r\\n]+"}
	}

	for _, s := range cutset {
		r := regexp.MustCompile(fmt.Sprintf(`(^%s)|(%s$)`, s, s))
		str = r.ReplaceAllString(str, "")
	}
	return str
}

// Md5 []byte转md5
func Md5(b []byte) string {
	return fmt.Sprintf("%x", md5.Sum(b))
}

// Md5Str string转md5
func Md5Str(str string) string {
	return Md5([]byte(str))
}

// Md5Any any转md5
func Md5Any(str any) string {
	return Md5([]byte(Str(str)))
}

// HmacMd5 []byte转md5
func HmacMd5(key, data []byte) string {
	h := hmac.New(md5.New, key)
	h.Write(data)
	return fmt.Sprintf("%x", h.Sum([]byte{}))
}

// HmacMd5Str string转md5
func HmacMd5Str(key, data string) string {
	return HmacMd5([]byte(key), []byte(data))
}

// HmacMd5Any any转md5
func HmacMd5Any(key, data any) string {
	return HmacMd5Str(Str(key), Str(data))
}

// Sha1 Sha1加密
func Sha1(b []byte) string {
	h := sha1.New()
	h.Write(b)
	return hex.EncodeToString(h.Sum(nil))
}

// Sha1Str Sha1加密
func Sha1Str(str string) string {
	return Sha1([]byte(str))
}

// Sha1Any Sha1加密
func Sha1Any(str any) string {
	return Sha1([]byte(Str(str)))
}

// Sha256 Sha256加密
func Sha256(b []byte) string {
	var h = sha256.New()
	h.Write(b)
	return fmt.Sprintf("%x", h.Sum(nil))
}

// Sha256Str Sha256加密
func Sha256Str(str string) string {
	return Sha256([]byte(str))
}

// Sha256Any Sha256加密
func Sha256Any(str any) string {
	return Sha256([]byte(Str(str)))
}

// HmacSha256 HmacSha256加密
func HmacSha256(secret, data []byte) string {
	h := hmac.New(sha256.New, secret)
	h.Write(data)
	return hex.EncodeToString(h.Sum(nil))
}

// HmacSha256Str HmacSha256加密
func HmacSha256Str(secret, data string) string {
	return HmacSha256([]byte(secret), []byte(data))
}

// HmacSha256Any HmacSha256加密
func HmacSha256Any(secret, data any) string {
	return HmacSha256([]byte(Str(secret)), []byte(Str(data)))
}

// UniqID 生成唯一字串
func UniqID() string {
	b := make([]byte, 48)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		return ""
	}
	return Md5([]byte(base64.URLEncoding.EncodeToString(b)))
}

// XID 生成唯一ID
func XID() string {
	return Md5([]byte(xid.New().String()))
}

// UUID 生成UUID
func UUID() string {
	return uuid.NewV4().String()
}
